/*
 * Copyright (c) 2022 Hunan OpenValley Digital Industry Development Co., Ltd.
 *
 * HDF is dual licensed: you can use it either under the terms of
 * the GPL, or the BSD license, at your option.
 * See the LICENSE file in the root of this repository for complete details.
 */
#include <stdlib.h>
#include "adc_core.h"
#include "adc_if.h"
#include "hcs_macro.h"
#include "hdf_config_macro.h"
#include "hdf_device_desc.h"
#include "hdf_log.h"

#define HDF_LOG_TAG "ESP32_HDF_ADC"

#define PLATFORM_CONFIG     HCS_NODE(HCS_ROOT, platform)
#define PLATFORM_ADC_CONFIG HCS_NODE(HCS_NODE(HCS_ROOT, platform), adc_config)
#define HDF_ADC_FIND_CONFIG(node, name, resource)        \
do {                                                     \
    if (strcmp(HCS_PROP(node, match_attr), name) == 0) { \
        resource->devNum = HCS_PROP(node, handle);       \
        resource->chanNum = HCS_PROP(node, channel);     \
        result = HDF_SUCCESS;                            \
    } else {                                             \
        result = HDF_FAILURE;                            \
    }                                                    \
} while (0)

static int32_t Esp32AdcRead(struct AdcDevice *device, uint32_t channel, uint32_t *val);
static int32_t Esp32AdcStart(struct AdcDevice *device);
static int32_t Esp32AdcStop(struct AdcDevice *device);

static int32_t Esp32AdcBind(struct HdfDeviceObject *device);
static int32_t Esp32AdcInit(struct HdfDeviceObject *device);
static void Esp32AdcRelease(struct HdfDeviceObject *device);

struct AdcMethod g_adcHdfMethod = {
    .start = Esp32AdcStart,
    .stop = Esp32AdcStop,
    .read = Esp32AdcRead,
};

struct HdfDriverEntry g_adcHdfDriverEntry = {
    .moduleVersion = 1,
    .moduleName = "ESP32_HDF_ADC",
    .Bind = Esp32AdcBind,
    .Init = Esp32AdcInit,
    .Release = Esp32AdcRelease,
};
HDF_INIT(g_adcHdfDriverEntry);

static int32_t Esp32AdcBind(struct HdfDeviceObject *device)
{
    (void *)device;
    return HDF_SUCCESS;
}

static int32_t Esp32AdcInit(struct HdfDeviceObject *device)
{
    if (device == NULL) {
        HDF_LOGE("Esp32AdcInit param is NULL!\r\n");
        return HDF_ERR_INVALID_PARAM;
    }

    int32_t result = HDF_FAILURE;
    struct AdcDevice *host = NULL;
    host = (struct AdcDevice *)OsalMemAlloc(sizeof(struct AdcDevice));
    if (host == NULL) {
        HDF_LOGE("[%s]: malloc host is NULL\r\n", __func__);
        return HDF_ERR_MALLOC_FAIL;
    }

    memset_s(host, sizeof(struct AdcDevice), 0, sizeof(struct AdcDevice));
    HCS_FOREACH_CHILD_VARGS(PLATFORM_ADC_CONFIG, HDF_ADC_FIND_CONFIG, device->deviceMatchAttr, host);
    if (result != HDF_SUCCESS) {
        HDF_LOGE("Esp32 adc HCS_FOREACH_CHILD_VARGS fail\r\n");
        return HDF_FAILURE;
    }
    host->ops = &g_adcHdfMethod;
    device->priv = (void *)host;

    if(AdcDeviceAdd(host) != HDF_SUCCESS) {
        HDF_LOGE("Esp32 AdcDeviceAdd fail\r\n");
        return HDF_FAILURE;     
    }

    return HDF_SUCCESS;
}

static void Esp32AdcRelease(struct HdfDeviceObject *device)
{
    AdcDeviceRemove((struct AdcDevice*)device->priv);
    OsalMemFree(device->priv);
    device->priv = NULL;
}

static int32_t Esp32AdcRead(struct AdcDevice *device, uint32_t channel, uint32_t *val)
{
    return (AdcDevRead(NULL, device->devNum, channel, val) == HDF_SUCCESS) ? HDF_SUCCESS : HDF_FAILURE;
}

static int32_t Esp32AdcStart(struct AdcDevice *device)
{
    return (AdcDevOpen(device->devNum) == HDF_SUCCESS) ? HDF_SUCCESS : HDF_FAILURE;
}

static int32_t Esp32AdcStop(struct AdcDevice *device)
{
    AdcDevClose(device->devNum);
    return HDF_SUCCESS;
}
